home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr48
/
cgterm10.zip
/
PIBASYN1.MOD
< prev
next >
Wrap
Text File
|
1993-04-01
|
59KB
|
1,031 lines
(*----------------------------------------------------------------------*)
(* PIBASYNC.PAS --- Asynchronous I/O for Turbo Pascal *)
(*----------------------------------------------------------------------*)
(* *)
(* Author: (c) 1985, 1986 by Philip R. Burns *)
(* *)
(* Version: 1.0 (January, 1985) *)
(* 2.0 (June, 1985) *)
(* 2.1 (July, 1985) *)
(* 3.0 (October, 1985) *)
(* 3.1 (October, 1985) *)
(* 3.2 (November, 1985) *)
(* 4.0 (October, 1986) *)
(* *)
(* Systems: For MSDOS/PC DOS on IBM PCs and close compatibles only. *)
(* *)
(* History: Some of these routines are based upon ones written by: *)
(* *)
(* Alan Bishop *)
(* C. J. Dunford *)
(* Michael Quinlan *)
(* Gene Harris *)
(* *)
(* I have cleaned up these other authors' code, fixed some *)
(* bugs, and added many new features. *)
(* *)
(* In particular, starting with v4.0 of PibTerm, both input *)
(* and output to the serial port is buffered and interrupt- *)
(* driven. Also, XON/XOFF support has been moved to the *)
(* serial port interrupt handler, which results in fewer *)
(* overrun problems. *)
(* *)
(* Suggestions for improvements or corrections are welcome. *)
(* *)
(* If you use this code in your own programs, please be nice *)
(* and give proper credit. *)
(* *)
(*----------------------------------------------------------------------*)
(* *)
(* Routines: *)
(* *)
(* BIOS_RS232_Init --- Use BIOS to initialize port *)
(* DOS_Set_Intrpt --- Set interrupt vector using DOS *)
(* DOS_Get_Intrpt --- Get interrupt vector using DOS *)
(* Async_Isr --- Com port interrupt service routine *)
(* Async_Init --- Performs initialization. *)
(* Async_Clear_Errors --- Clear pending serial port errors *)
(* Async_Reset_Port --- Resets UART parameters for port *)
(* Async_Open --- Sets up COM port *)
(* Async_Close --- Closes down COM port *)
(* Async_Carrier_Detect --- Checks for modem carrier detect *)
(* Async_Carrier_Drop --- Checks for modem carrier drop *)
(* Async_Buffer_Check --- Checks if character in COM buffer *)
(* Async_Term_Ready --- Toggles terminal ready status *)
(* Async_Receive --- Reads character from COM buffer *)
(* Async_Receive_With_Timeout *)
(* --- Receives char. with timeout check *)
(* Async_Ring_Detect --- If ringing detected *)
(* Async_Send --- Transmits char over COM port *)
(* Async_Send_String --- Sends string over COM port *)
(* Async_Send_String_With_Delays *)
(* --- Sends string with timed delays *)
(* Async_Send_Break --- Sends break (attention) signal *)
(* Async_Percentage_Used --- Returns percentage com buffer used *)
(* Async_Purge_Buffer --- Purges receive buffer *)
(* Async_Release_Buffers --- Free memory for serial port queues *)
(* Async_Setup_Port --- Define port base, IRQ, RS232 addr *)
(* Async_Stuff --- Insert char into receive buffer *)
(* *)
(*----------------------------------------------------------------------*)
(* *)
(* PIBASYNC.PAS was split into PIBASYN1.PAS and PIBASYN2.PAS at *)
(* version 3.2 of PibTerm, and into PIBASYN1, PIBASYN2, and *)
(* PIBASYN3 for v4.0 of PibTerm. *)
(* *)
(*----------------------------------------------------------------------*)
(*----------------------------------------------------------------------*)
(* BIOS_RS232_Init --- Initialize UART *)
(*----------------------------------------------------------------------*)
PROCEDURE BIOS_RS232_Init( ComPort, ComParm : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: BIOS_RS232_Init *)
(* *)
(* Purpose: Issues interrupt $14 to initialize the UART *)
(* *)
(* Calling Sequence: *)
(* *)
(* BIOS_RS232_Init( ComPort, ComParm : INTEGER ); *)
(* *)
(* ComPort --- Communications Port Number (0 thru 3) *)
(* ComParm --- Communications Parameter Word *)
(* *)
(* Calls: INTR (to perform BIOS interrupt $14) *)
(* *)
(*----------------------------------------------------------------------*)
VAR
Regs: RegPack;
Save: INTEGER;
BEGIN (* BIOS_RS232_Init *)
(* Set RS 232 base. *)
Save := MEM[$0:RS232_Base];
MEM[$0:RS232_Base] := Async_RS232;
(* Initialize port *)
WITH Regs DO
BEGIN
Ax := ComParm AND $00FF; (* AH=0; AL=ComParm *)
Dx := ComPort; (* Port number to use *)
INTR($14, Regs);
END;
(* Set RS 232 base back *)
MEM[$0:RS232_Base] := Save;
END (* BIOS_RS232_Init *);
(*----------------------------------------------------------------------*)
(* DOS_Set_Intrpt --- Call DOS to set interrupt vector *)
(*----------------------------------------------------------------------*)
PROCEDURE DOS_Set_Intrpt( V, S, O : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: DOS_Set_Intrpt *)
(* *)
(* Purpose: Calls DOS to set interrupt vector *)
(* *)
(* Calling Sequence: *)
(* *)
(* DOS_Set_Intrpt( V, S, O : INTEGER ); *)
(* *)
(* V --- interrupt vector number to set *)
(* S --- segment address of interrupt routine *)
(* O --- offset address of interrupt routine *)
(* *)
(* Calls: MSDOS (to set interrupt) *)
(* *)
(*----------------------------------------------------------------------*)
VAR
Regs : Regpack;
BEGIN (* DOS_Set_Intrpt *)
WITH Regs DO
BEGIN
Ax := $2500 + ( V AND $00FF );
Ds := S;
Dx := O;
Es := 0;
MsDos( Regs );
END;
END (* DOS_Set_Intrpt *);
(*----------------------------------------------------------------------*)
(* DOS_Get_Intrpt --- Call DOS to get interrupt vector *)
(*----------------------------------------------------------------------*)
PROCEDURE DOS_Get_Intrpt( V: INTEGER; VAR S: INTEGER; VAR O : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: DOS_Get_Intrpt *)
(* *)
(* Purpose: Calls DOS to get interrupt vector *)
(* *)
(* Calling Sequence: *)
(* *)
(* DOS_Get_Intrpt( V, S, O : INTEGER ); *)
(* *)
(* V --- interrupt vector number to get *)
(* S --- segment address of interrupt routine *)
(* O --- offset address of interrupt routine *)
(* *)
(* Calls: MSDOS (to get interrupt) *)
(* *)
(*----------------------------------------------------------------------*)
VAR
Regs : Regpack;
BEGIN (* DOS_Get_Intrpt *)
Regs.Ax := $3500 + ( V AND $00FF );
MsDos( Regs );
S := Regs.Es;
O := Regs.Bx;
END (* DOS_Get_Intrpt *);
(*----------------------------------------------------------------------*)
(* Async_Isr --- Interrupt Service Routine *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Isr;
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Isr *)
(* *)
(* Purpose: Invoked when serial port interrupt occurs. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Isr; *)
(* *)
(* --- Called asynchronously only!!!!!! *)
(* *)
(*----------------------------------------------------------------------*)
BEGIN (* Async_Isr *)
INLINE(
$FB { STI ;Allow interrupts}
{;}
/$50 { PUSH AX ;Save registers}
/$53 { PUSH BX}
/$51 { PUSH CX}
/$52 { PUSH DX}
/$1E { PUSH DS}
/$56 { PUSH SI}
/$06 { PUSH ES}
/$57 { PUSH DI}
{;}
{; Get data segment for addressing global variables}
{;}
/$2E/$8E/$1E/>ASYNC_DSEG_SAVE { CS: MOV DS,[>Async_DSeg_Save]}
{;}
{; Begin major polling loop over pending interrupts.}
{;}
{; The polling loop is needed because the 8259 cannot handle another 8250}
{; interrupt while we service this interrupt. We keep polling here as long}
{; as an interrupt is received.}
{;}
/$8B/$16/>ASYNC_UART_IIR {Poll: MOV DX,[>Async_Uart_IIR] ;Get Interrupt ident register}
/$EC { IN AL,DX ;Pick up interrupt type}
{;}
/$A8/$01 { TEST AL,1 ;See if any interrupt signalled.}
/$74/$03 { JZ Polla ;Yes --- continue}
/$E9/$7F/$01 { JMP NEAR Back ;No --- return to invoker}
{;}
{; Determine type of interrupt.}
{; Possibilities:}
{;}
{; 0 = Modem status changed}
{; 2 = Transmit hold register empty (write char)}
{; 4 = Character received from port}
{; 6 = Line status changed}
{;}
/$24/$06 {Polla: AND AL,6 ;Strip unwanted bits from interrupt type}
/$3C/$04 { CMP AL,4 ;Check if interrupt >= 4}
/$74/$03 { JE Pollb ;}
/$E9/$A1/$00 { JMP NEAR Int2}
{;}
{; Write interrupts must be turned on if a higher-priority interrupt}
{; has been received, else the characters may not be sent (and a lockup}
{; may occur).}
{;}
/$50 {Pollb: PUSH AX ;Save interrupt type}
/$E8/$65/$01 { CALL EnabWI ;Enable write interrupts}
/$58 { POP AX ;Restore interrupt type}
{;}
{; --- Received a character ----}
{;}
/$3C/$04 {Int4: CMP AL,4 ;Check for received char interrupt}
/$74/$03 { JE Int4a ;Yes -- process it.}
/$E9/$95/$00 { JMP NEAR Int2 ;No -- skip.}
{;}
{; Read the character from the serial port.}
{;}
/$8B/$16/>ASYNC_BASE {Int4a: MOV DX,[>Async_Base] ;Read character from port}
/$EC { IN AL,DX}
{;}
{; Check if XON/XOFF honored. If so, check if incoming character is}
{; an XON or an XOFF.}
{;}
/$F6/$06/>ASYNC_DO_XONXOFF/$01 { TEST BYTE [<Async_Do_XonXoff],1 ;See if we honor XON/XOFF}
/$74/$25 { JZ Int4d ;No -- skip XON/XOFF checks}
{;}
/$3C/<XON { CMP AL,<XON ;See if XON found}
/$74/$11 { JE Int4b ;Skip if XON found}
/$3C/<XOFF { CMP AL,<XOFF ;See if XOFF found}
/$75/$1D { JNE Int4d ;Skip if XOFF not found}
{;}
{; XOFF received -- set flag indicating sending of chars isn't possible}
{;}
/$C6/$06/>ASYNC_XOFF_RECEIVED/$01 { MOV BYTE [<Async_XOFF_Received],1 ;Turn on received XOFF flag}
/$C6/$06/>ASYNC_XOFF_REC_DISPLAY/$01{ MOV BYTE [<Async_XOFF_Rec_Display],1 ;Turn on display flag}
/$E9/$BE/$FF { JMP NEAR Poll}
{;}
{; XON received -- allow more characters to be sent.}
{;}
/$C6/$06/>ASYNC_XOFF_RECEIVED/$00 {Int4b: MOV BYTE [<Async_XOFF_Received],0 ;Turn off received XOFF flag}
/$C6/$06/>ASYNC_XON_REC_DISPLAY/$01{ MOV BYTE [<Async_XON_Rec_Display],1 ;Turn on display flag}
{;}
/$E8/$2F/$01 { CALL EnabWI ;Enable write interrupts}
/$E9/$61/$00 { JMP NEAR Int4z}
{;}
{; Not XON/XOFF -- handle other character.}
{;}
/$F6/$06/>ASYNC_LINE_STATUS/$02{Int4d: TEST BYTE [>Async_Line_Status],2 ;Check for buffer overrun}
/$75/$5A { JNZ Int4z ;Yes --- don't store anything}
{;}
/$8B/$1E/>ASYNC_BUFFER_HEAD { MOV BX,[>Async_Buffer_Head] ;Current position in input buffer}
/$C4/$3E/>ASYNC_BUFFER_PTR { LES DI,[>Async_Buffer_Ptr] ;Pick up buffer address}
/$01/$DF { ADD DI,BX ;Update position}
/$26/$88/$05 { ES: MOV [DI],AL ;Store received character in buffer}
/$FF/$06/>ASYNC_BUFFER_USED { INC WORD [>Async_Buffer_Used] ;Increment count of chars in buffer}
{;}
/$A1/>ASYNC_BUFFER_USED { MOV AX,[>Async_Buffer_Used] ;Pick up buffer usage count}
/$3B/$06/>ASYNC_MAXBUFFERUSED { CMP AX,[>Async_MaxBufferUsed] ;See if greater usage than ever before}
/$7E/$03 { JLE Int4f ;Skip if not}
/$A3/>ASYNC_MAXBUFFERUSED { MOV [>Async_MaxBufferUsed],AX ;This is greatest use thus far}
{;}
/$43 {Int4f: INC BX ;Increment buffer pointer}
/$3B/$1E/>ASYNC_BUFFER_SIZE { CMP BX,[>Async_Buffer_Size] ;Check if past end of buffer}
/$7E/$02 { JLE Int4h}
/$31/$DB { XOR BX,BX ;If so, wrap around to front}
{;}
/$39/$1E/>ASYNC_BUFFER_TAIL {Int4h: CMP WORD [>Async_Buffer_Tail],BX ;Check for overflow}
/$74/$29 { JE Int4s ;Jump if head ran into tail}
{;}
/$89/$1E/>ASYNC_BUFFER_HEAD { MOV [>Async_Buffer_Head],BX ;Update head pointer}
{;}
{; If XON/XOFF available, and buffer getting full, set up to send}
{; XOFF to remote system.}
{;}
{; This happens in two possible stages:}
{;}
{; (1) An XOFF is sent right when the buffer becomes 'Async_Buffer_High'}
{; characters full.}
{;}
{; (2) A second XOFF is sent right when the buffer becomes}
{; 'Async_Buffer_High_2' characters full; this case is likely the}
{; result of the remote not having seen our XOFF because it was}
{; lost in transmission.}
{;}
/$F6/$06/>ASYNC_DO_XONXOFF/$01 { TEST BYTE [<Async_Do_XonXoff],1 ;See if we honor XON/XOFF}
/$74/$23 { JZ Int4z ;No -- skip XON/XOFF checks}
{;}
{; Check against first high-water mark.}
{;}
/$3B/$06/>ASYNC_BUFFER_HIGH { CMP AX,[>Async_Buffer_High] ;AX still has Async_Buffer_Used}
/$7C/$1D { JL Int4z ;Not very full, so keep going.}
{;}
{; Check if we've already sent XOFF.}
{;}
/$F6/$06/>ASYNC_XOFF_SENT/$01 { TEST BYTE [<Async_XOFF_Sent],1 ;Remember if we sent XOFF or not}
/$74/$06 { JZ Int4j ;No -- go send it now.}
{;}
{; Check against second high-water mark.}
{; If we are right at it, send an XOFF regardless of whether we've}
{; already sent one or not. (Perhaps the first got lost.)}
{;}
/$3B/$06/>ASYNC_BUFFER_HIGH_2 { CMP AX,[>Async_Buffer_High_2]}
/$75/$10 { JNE Int4z ;Not at 2nd mark -- skip}
{;}
/$C6/$06/>ASYNC_SEND_XOFF/$01 {Int4j: MOV BYTE [<Async_Send_XOFF],1 ;Indicate we need to send XOFF}
/$E8/$D3/$00 { CALL EnabWI ;Ensure write interrupts enabled}
/$E9/$52/$FF { JMP NEAR Poll ;}
{;}
{; If we come here, then the input buffer has overflowed.}
{; Characters will be thrown away until the buffer empties at least one slot.}
{;}
/$80/$0E/>ASYNC_LINE_STATUS/$02 {Int4s: OR BYTE PTR [>Async_Line_Status],2 ;Flag overrun}
{;}
/$E9/$4A/$FF {Int4z: JMP NEAR Poll}
{;}
{; --- Write a character ---}
{;}
/$3C/$02 {Int2: CMP AL,2 ;Check for THRE interrupt}
/$74/$03 { JE Int2a ;Yes -- process it.}
/$E9/$97/$00 { JMP NEAR Int6 ;No -- skip.}
{;}
{; Check first if we need to send an XOFF to remote system.}
{;}
/$F6/$06/>ASYNC_SEND_XOFF/$01 {Int2a: TEST BYTE [<Async_Send_Xoff],1 ;See if we are sending XOFF}
/$74/$34 { JZ Int2d ;No -- skip it}
{;}
{; Yes, we are to send XOFF to remote.}
{;}
{; First, check DSR and CTS as requested.}
{; If those status lines aren't ready, turn off write interrupts and}
{; try later, after a line status change.}
{;}
/$F6/$06/>ASYNC_DO_DSR/$01 { TEST BYTE [<Async_Do_DSR],1 ;See if DSR checking required}
/$74/$09 { JZ Int2b ;No -- skip it}
{;}
/$8B/$16/>ASYNC_UART_MSR { MOV DX,[>Async_Uart_MSR] ;Get modem status register}
/$EC { IN AL,DX}
/$A8/<ASYNC_DSR { TEST AL,<Async_DSR ;Check for Data Set Ready}
/$74/$2E { JZ Int2e ;If not DSR, turn off write interrupts}
{;}
/$F6/$06/>ASYNC_DO_CTS/$01 {Int2b: TEST BYTE [<Async_Do_CTS],1 ;See if CTS checking required}
/$74/$09 { JZ Int2c ;No -- skip it}
{;}
/$8B/$16/>ASYNC_UART_MSR { MOV DX,[>Async_Uart_MSR] ;Get modem status register}
/$EC { IN AL,DX}
/$A8/<ASYNC_CTS { TEST AL,<Async_CTS ;Check for Clear To Send}
/$74/$1E { JZ Int2e ;If not CTS, turn off write ints}
{;}
{; All status lines look OK.}
{; Send the XOFF.}
{;}
/$B0/<XOFF {Int2c: MOV AL,<XOFF ;Get XOFF Character}
/$8B/$16/>ASYNC_BASE { MOV DX,[>Async_Base] ;Get transmit hold register address}
/$EE { OUT DX,AL ;Output the XOFF}
/$C6/$06/>ASYNC_SEND_XOFF/$00 { MOV BYTE [<Async_Send_XOFF],0 ;Turn off send XOFF flag}
/$C6/$06/>ASYNC_XOFF_SENT/$01 { MOV BYTE [<Async_XOFF_Sent],1 ;Turn on sent XOFF flag}
/$E9/$08/$FF { JMP NEAR Poll ;Return}
{;}
{; Not sending XOFF -- see if any character in buffer to be sent.}
{;}
/$8B/$1E/>ASYNC_OBUFFER_TAIL {Int2d: MOV BX,[>Async_OBuffer_Tail] ;Pick up output buffer pointers}
/$3B/$1E/>ASYNC_OBUFFER_HEAD { CMP BX,[>Async_OBuffer_Head]}
/$75/$0B { JNE Int2m ;Skip if not equal --> something to send}
{;}
{; If nothing to send, turn off write interrupts to avoid unnecessary}
{; time spent handling useless THRE interrupts.}
{;}
/$8B/$16/>ASYNC_UART_IER {Int2e: MOV DX,[>Async_Uart_IER] ;If nothing -- or can't -- send ...}
/$EC { IN AL,DX ;}
/$24/$FD { AND AL,$FD ;}
/$EE { OUT DX,AL ;... disable write interrupts}
/$E9/$F3/$FE { JMP NEAR Poll ;}
{;}
{; If something to send, ensure that remote system didn't send us XOFF.}
{; If it did, we can't send anything, so turn off write interrupts and}
{; wait for later (after an XON has been received).}
{;}
/$F6/$06/>ASYNC_XOFF_RECEIVED/$01 {Int2m: TEST BYTE [<Async_XOFF_Received],1 ;See if we received XOFF}
/$75/$EE { JNZ Int2e ;Yes -- can't send anything now}
{;}
{; If we can send character, check DSR and CTS as requested.}
{; If those status lines aren't ready, turn off write interrupts and}
{; try later, after a line status change.}
{;}
/$8B/$16/>ASYNC_UART_MSR { MOV DX,[>Async_Uart_MSR] ;Otherwise get modem status}
/$EC { IN AL,DX}
/$A2/>ASYNC_MODEM_STATUS { MOV [>Async_Modem_Status],AL ;and save modem status for later}
{;}
/$F6/$06/>ASYNC_DO_DSR/$01 { TEST BYTE [<Async_Do_DSR],1 ;See if DSR checking required}
/$74/$04 { JZ Int2n ;No -- skip it}
{;}
/$A8/<ASYNC_DSR { TEST AL,<Async_DSR ;Check for Data Set Ready}
/$74/$DB { JZ Int2e ;If not DSR, turn off write ints}
{;}
/$F6/$06/>ASYNC_DO_CTS/$01 {Int2n: TEST BYTE [<Async_Do_CTS],1 ;See if CTS checking required}
/$74/$04 { JZ Int2o ;No -- skip it}
{;}
/$A8/<ASYNC_CTS { TEST AL,<Async_CTS ;Check for Clear To Send}
/$74/$D0 { JZ Int2e ;If not CTS, turn off write ints}
{;}
{; Everything looks OK for sending, so send the character.}
{;}
/$C4/$3E/>ASYNC_OBUFFER_PTR {Int2o: LES DI,[>Async_OBuffer_Ptr] ;Get output buffer pointer}
/$01/$DF { ADD DI,BX ;Position to character to output}
/$26/$8A/$05 { ES: MOV AL,[DI] ;Get character to output}
/$8B/$16/>ASYNC_BASE { MOV DX,[>Async_Base] ;Get transmit hold register address}
/$EE { OUT DX,AL ;Output the character}
{;}
/$FF/$0E/>ASYNC_OBUFFER_USED { DEC WORD [>Async_OBuffer_Used] ;Decrement count of chars in buffer}
/$43 { INC BX ;Increment tail pointer}
/$3B/$1E/>ASYNC_OBUFFER_SIZE { CMP BX,[>Async_OBuffer_Size] ;See if past end of buffer}
/$7E/$02 { JLE Int2z}
/$31/$DB { XOR BX,BX ;If so, wrap to front}
{;}
/$89/$1E/>ASYNC_OBUFFER_TAIL {Int2z: MOV [>Async_OBuffer_Tail],BX ;Store updated buffer tail}
/$E9/$AC/$FE { JMP NEAR Poll}
{;}
{; --- Line status change ---}
{;}
/$3C/$06 {Int6: CMP AL,6 ;Check for line status interrupt}
/$75/$11 { JNE Int0 ;No -- skip.}
{;}
/$8B/$16/>ASYNC_UART_LSR { MOV DX,[>Async_Uart_LSR] ;Yes -- pick up line status register}
/$EC { IN AL,DX ;and its contents}
/$24/$1E { AND AL,$1E ;Strip unwanted bits}
/$A2/>ASYNC_LINE_STATUS { MOV [>Async_Line_Status],AL ;Store for future reference}
/$08/$06/>ASYNC_LINE_ERROR_FLAGS { OR [>Async_Line_Error_Flags],AL ;Add to any past transgressions}
/$E9/$97/$FE { JMP NEAR Poll}
{;}
{; --- Modem status change ---}
{;}
/$3C/$00 {Int0: CMP AL,0 ;Check for modem status change}
/$74/$03 { JE Int0a ;Yes -- handle it}
/$E9/$90/$FE { JMP NEAR Poll ;Else get next interrupt}
{;}
/$8B/$16/>ASYNC_UART_MSR {Int0a: MOV DX,[>Async_Uart_MSR] ;Pick up modem status reg. address}
/$EC { IN AL,DX ;and its contents}
/$A2/>ASYNC_MODEM_STATUS { MOV [>Async_Modem_Status],AL ;Store for future reference}
/$E8/$03/$00 { CALL EnabWI ;Turn on write interrupts, in case}
{; ;status change resulted from CTS/DSR}
{; ;changing state.}
/$E9/$82/$FE { JMP NEAR Poll}
{;}
{; Internal subroutine to enable write interrupts.}
{;}
{EnabWI: ;PROC NEAR}
/$8B/$16/>ASYNC_UART_IER { MOV DX,[>Async_Uart_IER] ;Get interrupt enable register}
/$EC { IN AL,DX ;Check contents of IER}
/$A8/$02 { TEST AL,2 ;See if write interrupt enabled}
/$75/$03 { JNZ EnabRet ;Skip if so}
/$0C/$02 { OR AL,2 ;Else enable write interrupts ...}
/$EE { OUT DX,AL ;... by rewriting IER contents}
/$C3 {EnabRet: RET ;Return to caller}
{;}
{; Send non-specific EOI to 8259 controller.}
{;}
/$B0/$20 {Back: MOV AL,$20 ;EOI = $20}
/$E6/$20 { OUT $20,AL}
{;}
{; Restore registers}
{;}
/$5F { POP DI}
/$07 { POP ES}
/$5E { POP SI}
/$1F { POP DS}
/$5A { POP DX}
/$59 { POP CX}
/$5B { POP BX}
/$58 { POP AX}
/$89/$EC { MOV SP,BP}
/$5D { POP BP}
/$CF { IRET}
);
END (* Async_Isr *);
(*----------------------------------------------------------------------*)
(* Async_Init --- Initialize Asynchronous Variables *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Init( Async_Buffer_Max : INTEGER;
Async_OBuffer_Max : INTEGER;
Async_High_Lev1 : INTEGER;
Async_High_Lev2 : INTEGER;
Async_Low_Lev : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Init *)
(* *)
(* Purpose: Initializes variables *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Init( Async_Buffer_Max : INTEGER; *)
(* Async_OBuffer_Max : INTEGER; *)
(* Async_High_Lev1 : INTEGER; *)
(* Async_High_Lev2 : INTEGER; *)
(* Async_Low_Lev : INTEGER ); *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
(* STRUCTURED *) CONST
Default_Com_Base : ARRAY[1..MaxComPorts] OF INTEGER =
( COM1_Base, COM2_Base, COM3_Base, COM4_Base );
Default_Com_Irq : ARRAY[1..MaxComPorts] OF INTEGER =
( COM1_Irq, COM2_Irq, COM3_Irq, COM4_Irq );
Default_Com_RS232: ARRAY[1..MaxComPorts] OF INTEGER =
( COM1_RS232, COM2_RS232, COM3_RS232, COM4_RS232 );
VAR
I: INTEGER;
BEGIN (* Async_Init *)
(* Save data segment address in code *)
(* segment for use in serial port *)
(* interrupt handler. *)
Async_DSeg_Save := DSeg;
(* No port open yet. *)
Async_Open_Flag := FALSE;
(* No XON/XOFF handling yet. *)
Async_XOFF_Sent := FALSE;
Async_XOFF_Received := FALSE;
Async_XOFF_Rec_Display := FALSE;
Async_XON_Rec_Display := FALSE;
Async_Send_XOFF := FALSE;
(* Set up empty receive buffer *)
Async_Buffer_Overflow := FALSE;
Async_Buffer_Used := 0;
Async_MaxBufferUsed := 0;
Async_Buffer_Head := 0;
Async_Buffer_Tail := 0;
(* Set up empty send buffer. *)
Async_OBuffer_Overflow := FALSE;
Async_OBuffer_Used := 0;
Async_MaxOBufferUsed := 0;
Async_OBuffer_Head := 0;
Async_OBuffer_Tail := 0;
(* Set default wait time for output *)
(* buffer to drain when it fills up. *)
Async_Output_Delay := 500;
(* No modem or line errors yet. *)
Async_Line_Status := 0;
Async_Modem_Status := 0;
Async_Line_Error_Flags := 0;
(* Get buffer sizes *)
IF ( Async_Buffer_Max > 0 ) THEN
Async_Buffer_Size := Async_Buffer_Max - 1
ELSE
Async_Buffer_Size := 4095;
IF ( Async_OBuffer_Max > 0 ) THEN
Async_OBuffer_Size := Async_OBuffer_Max - 1
ELSE
Async_OBuffer_Size := 1131;
(* Get receive buffer overflow *)
(* check-points. *)
IF ( Async_Low_Lev > 0 ) THEN
Async_Buffer_Low := Async_Low_Lev
ELSE
Async_Buffer_Low := Async_Buffer_Size DIV 4;
IF ( Async_High_Lev1 > 0 ) THEN
Async_Buffer_High := Async_High_Lev1
ELSE
Async_Buffer_High := ( Async_Buffer_Size DIV 4 ) * 3;
IF ( Async_High_Lev2 > 0 ) THEN
Async_Buffer_High_2 := Async_High_Lev2
ELSE
Async_Buffer_High_2 := ( Async_Buffer_Size DIV 10 ) * 9;
(* Allocate buffers *)
GETMEM( Async_Buffer_Ptr , Async_Buffer_Size + 1 );
GETMEM( Async_OBuffer_Ptr , Async_OBuffer_Size + 1 );
(* No UART addresses defined yet *)
Async_Uart_IER := 0;
Async_Uart_IIR := 0;
Async_Uart_MSR := 0;
Async_Uart_LSR := 0;
(* Set default port addresses *)
(* and default IRQ lines *)
FOR I := 1 TO MaxComPorts DO
BEGIN
Com_Base[I] := Default_Com_Base [I];
Com_Irq [I] := Default_Com_Irq [I];
Com_RS232[I] := Default_Com_RS232[I];
END;
END (* Async_Init *);
(*----------------------------------------------------------------------*)
(* Async_Close --- Close down communications interrupts *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Close( Drop_DTR: BOOLEAN );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Close *)
(* *)
(* Purpose: Resets interrupt system when UART interrupts *)
(* are no longer needed. *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Close( Drop_DTR : BOOLEAN ); *)
(* *)
(* Drop_DTR --- TRUE to drop DTR when closing down port *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
VAR
I : INTEGER;
M : INTEGER;
BEGIN (* Async_Close *)
IF Async_Open_Flag THEN
BEGIN
(* disable the IRQ on the 8259 *)
INLINE($FA); (* disable interrupts *)
I := Port[I8088_IMR]; (* get the interrupt mask register *)
M := 1 SHL Async_Irq; (* set mask to turn off interrupt *)
Port[I8088_IMR] := I OR M;
(* disable the 8250 interrupts *)
Port[UART_IER + Async_Base] := 0;
(* Disable OUT2, RTS, OUT1 on the 8250, but *)
(* possibly leave DTR enabled. *)
IF Drop_Dtr THEN
Port[UART_MCR + Async_Base] := 0
ELSE
Port[UART_MCR + Async_Base] := 1;
INLINE($FB); (* enable interrupts *)
(* re-initialize our data areas so we know *)
(* the port is closed *)
Async_Open_Flag := FALSE;
Async_XOFF_Sent := FALSE;
(* Restore the previous interrupt pointers *)
DOS_Set_Intrpt( Async_Irq + 8 , Async_Save_Iaddr[1],
Async_Save_Iaddr[2] );
END;
END (* Async_Close *);
(*----------------------------------------------------------------------*)
(* Async_Clear_Errors --- Reset pending errors in async port *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Clear_Errors;
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Clear_Errors *)
(* *)
(* Purpose: Resets pending errors in async port *)
(* *)
(* Calling sequence: *)
(* *)
(* Async_Clear_Errors; *)
(* *)
(* Calls: None *)
(* *)
(*----------------------------------------------------------------------*)
VAR
I: INTEGER;
M: INTEGER;
BEGIN (* Async_Clear_Errors *)
(* Read the RBR and reset any pending error conditions. *)
(* First turn off the Divisor Access Latch Bit to allow *)
(* access to RBR, etc. *)
INLINE($FA); (* disable interrupts *)
Port[UART_LCR + Async_Base] := Port[UART_LCR + Async_Base] AND $7F;
(* Read the Line Status Register to reset any errors *)
(* it indicates *)
I := Port[UART_LSR + Async_Base];
(* Read the Receiver Buffer Register in case it *)
(* contains a character *)
I := Port[UART_RBR + Async_Base];
(* enable the irq on the 8259 controller *)
I := Port[I8088_IMR]; (* get the interrupt mask register *)
M := (1 SHL Async_Irq) XOR $00FF;
Port[I8088_IMR] := I AND M;
(* enable OUT2 on 8250 *)
I := Port[UART_MCR + Async_Base];
Port[UART_MCR + Async_Base] := I OR $0B;
(* enable the data ready interrupt on the 8250 *)
Port[UART_IER + Async_Base] := $0F;
(* Re-enable 8259 *)
Port[$20] := $20;
INLINE($FB); (* enable interrupts *)
END (* Async_Clear_Errors *);
(*----------------------------------------------------------------------*)
(* Async_Reset_Port --- Set/reset communications port parameters *)
(*----------------------------------------------------------------------*)
PROCEDURE Async_Reset_Port( ComPort : INTEGER;
BaudRate : INTEGER;
Parity : CHAR;
WordSize : INTEGER;
StopBits : INTEGER );
(*----------------------------------------------------------------------*)
(* *)
(* Procedure: Async_Reset_Port *)
(* *)
(* Purpose: Resets communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Async_Reset_Port( ComPort : INTEGER; *)
(* BaudRate : INTEGER; *)
(* Parity : CHAR; *)
(* WordSize : INTEGER; *)
(* StopBits : INTEGER); *)
(* *)
(* ComPort --- which port (1, 2, 3) *)
(* BaudRate --- Baud rate (110 to 19200) *)
(* Parity --- "E" for even, "O" for odd, "N" for none *)
(* WordSize --- Bits per character (5 through 8) *)
(* StopBits --- How many stop bits (1 or 2) *)
(* *)
(* Calls: *)
(* *)
(* Async_Clear_Errors --- Clear async line errors *)
(* *)
(*----------------------------------------------------------------------*)
CONST (* Baud Rate Constants *)
Async_Num_Bauds = 9;
Async_Baud_Table : ARRAY [1..Async_Num_Bauds] OF RECORD
Baud, Bits : INTEGER;
END
= ( ( Baud: 110; Bits: $00 ),
( Baud: 150; Bits: $20 ),
( Baud: 300; Bits: $40 ),
( Baud: 600; Bits: $60 ),
( Baud: 1200; Bits: $80 ),
( Baud: 2400; Bits: $A0 ),
( Baud: 4800; Bits: $C0 ),
( Baud: 9600; Bits: $E0 ),
( Baud: 19200; Bits: $E0 ) );
VAR
I : INTEGER;
M : INTEGER;
ComParm : INTEGER;
BEGIN (* Async_Reset_Port *)
(*---------------------------------------------------*)
(* Build the ComParm for RS232_Init *)
(* See Technical Reference Manual for description *)
(*---------------------------------------------------*)
(* Set up the bits for the baud rate *)
IF ( BaudRate > Async_Baud_Table[Async_Num_Bauds].Baud ) THEN
BaudRate := Async_Baud_Table[Async_Num_Bauds].Baud
ELSE IF ( BaudRate < Async_Baud_Table[1].Baud ) THEN
BaudRate := Async_Baud_Table[1].Baud;
(* Remember baud rate for purges *)
Async_Baud_Rate := BaudRate;
I := 0;
REPEAT
I := I + 1
UNTIL ( ( I >= Async_Num_Bauds ) OR
( BaudRate = Async_Baud_Table[I].Baud ) );
ComParm := Async_Baud_Table[I].Bits;
(* Choose Parity *)
CASE UpCase(Parity) OF
'E' : ComParm := ComParm OR $0018;
'O' : ComParm := ComParm OR $0008;
'M' : ComParm := ComParm OR $0020;
'S' : ComParm := ComParm OR $0038;
ELSE ;
END (* CASE *);
(* Choose number of data bits *)
WordSize := WordSize - 5;
IF ( WordSize < 0 ) OR ( WordSize > 3 ) THEN
WordSize := 3;
ComParm := ComParm OR WordSize;
(* Choose stop bits *)
IF StopBits = 2 THEN
ComParm := ComParm OR $0004; (* default is 1 stop bit *)
(* Use the BIOS COM port init routine *)
BIOS_RS232_Init( ComPort - 1 , ComParm );
(* If > 9600 baud, we have to screw *)
(* around a bit *)
IF ( BaudRate = 19200 ) THEN
BEGIN
I := PORT[ UART_LCR + Async_Base ];
PORT[ UART_LCR + Async_Base ] := I OR $80;
PORT[ UART_THR + Async_Base ] := 6;
PORT[ UART_IER + Async_Base ] := 0;
I := PORT[ UART_LCR + Async_Base ];
PORT[ UART_LCR + Async_Base ] := I AND $7F;
END;
(* Clear any pending errors on *)
(* async line *)
Async_Clear_Errors;
END (* Async_Reset_Port *);
(*----------------------------------------------------------------------*)
(* Async_Open --- Open communications port *)
(*----------------------------------------------------------------------*)
FUNCTION Async_Open( ComPort : INTEGER;
BaudRate : INTEGER;
Parity : CHAR;
WordSize : INTEGER;
StopBits : INTEGER ) : BOOLEAN;
(*----------------------------------------------------------------------*)
(* *)
(* Function: Async_Open *)
(* *)
(* Purpose: Opens communications port *)
(* *)
(* Calling Sequence: *)
(* *)
(* Flag := Async_Open( ComPort : INTEGER; *)
(* BaudRate : INTEGER; *)
(* Parity : CHAR; *)
(* WordSize : INTEGER; *)
(* StopBits : INTEGER) : BOOLEAN; *)
(* *)
(* ComPort --- which port (1 though 3) *)
(* BaudRate --- Baud rate (110 to 19200) *)
(* Parity --- "E" for even, "O" for odd, "N" for none, *)
(* "S" for space, "M" for mark. *)
(* WordSize --- Bits per character (5 through 8) *)
(* StopBits --- How many stop bits (1 or 2) *)
(* *)
(* Flag returned TRUE if port initialized successfully; *)
(* Flag returned FALSE if any errors. *)
(* *)
(* Calls: *)
(* *)
(* Async_Reset_Port --- initialize RS232 port *)
(* DOS_Set_Intrpt --- set address of RS232 interrupt routine *)
(* *)
(*----------------------------------------------------------------------*)
BEGIN (* Async_Open *)
(* If port open, close it down first. *)
IF Async_Open_Flag THEN
Async_Close( FALSE );
(* Choose communications port *)
IF ( ComPort < 1 ) THEN
ComPort := 1
ELSE IF ( ComPort > MaxComPorts ) THEN
ComPort := MaxComPorts;
Async_Port := ComPort;
Async_Base := Com_Base [ ComPort ];
Async_Irq := Com_Irq [ ComPort ];
Async_RS232 := Com_RS232[ ComPort ];
(* Set register pointers for ISR routine *)
Async_Uart_IER := Async_Base + UART_IER;
Async_Uart_IIR := Async_Base + UART_IIR;
Async_Uart_MSR := Async_Base + UART_MSR;
Async_Uart_LSR := Async_Base + UART_LSR;
(* Check if given port installed *)
IF (Port[UART_IIR + Async_Base] and $00F8) <> 0 THEN
Async_Open := FALSE (* Serial port not installed *)
ELSE
BEGIN (* Open the port *)
(* Get current interrupt address *)
DOS_Get_Intrpt( Async_Irq + 8 , Async_Save_Iaddr[1],
Async_Save_Iaddr[2] );
(* Set interrupt routine address *)
DOS_Set_Intrpt( Async_Irq + 8 , CSeg , Ofs( Async_Isr ) );
(* Set up UART *)
Async_Reset_Port( ComPort, BaudRate, Parity, WordSize, StopBits );
Async_Open := TRUE;
Async_Open_Flag := TRUE;
END;
END (* Async_Open *);